/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 image_sequence.h

	$Header: /heroes4/image_sequence.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( IMAGE_SEQUENCE_H_INCLUDED )
#define IMAGE_SEQUENCE_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#pragma warning( disable: 4786 ) // Disable the ever so helpful "identifier was truncated to '255' characters" warning

#include <cassert>
#include <iosfwd>
#include <limits>
#include <list>
#include <vector>

#include "bitmap_group.h"
#include "resource_traits.h"
#include "scale_bitmap.h"
#include "screen_point.h"

class t_image_sequence;

enum t_image_sequence_types
{
	k_image_sequence_standard			= 1,
	k_image_sequence_delta_compressed	= 2
};

const int k_image_no_base_frame			= -1;

extern bool g_disable_base_frames_drawing ;
extern bool g_disable_base_shadow_drawing ;
extern bool g_disable_shadow_drawing ;
extern bool g_disable_frames_drwaing ;

// ------------------------------------------------------------------------------
// t_image_sequence_frame_info class template
// ------------------------------------------------------------------------------

class t_image_sequence_frame_info
{
public:
	// Data members
	t_uint32 index;
	t_uint32 shadow_index;

	t_image_sequence_frame_info();
};

inline t_image_sequence_frame_info::t_image_sequence_frame_info()
	:	index( std::numeric_limits< t_uint32 >::max() ),
		shadow_index( std::numeric_limits< t_uint32 >::max() )
{
}

// ------------------------------------------------------------------------------
// t_image_sequence_base_base class
// ------------------------------------------------------------------------------

class t_image_sequence_base_base
{
public:
	// Static member functions
	static t_uint32 empty_frame_index() { return std::numeric_limits< t_uint32 >::max(); }

	// Destructor
	virtual ~t_image_sequence_base_base();

	// Member functions
	void			delete_frame( int frame_num );
	void			delete_shadow( int frame_num );
	void			duplicate_frame( int frame_num );
	void			duplicate_shadow( int frame_num );
	t_int16			get_base_frame() const;
	t_int16			get_base_shadow() const;
	int				get_frame_count() const;
	t_uint32		get_frame_index( int frame_num ) const;
	t_screen_point	get_offset() const;
	t_screen_rect	get_rect() const;
	t_screen_rect	get_rect( int frame_num ) const;
	t_uint16		get_sequence_type() const;
	t_uint32		get_shadow_index( int frame_num ) const;
	t_screen_rect	get_shadow_rect() const;
	t_screen_rect	get_shadow_rect( int frame_num ) const;
	bool			hit_test( int frame_num, t_screen_point const & point ) const;
	void			move_frame( int source_frame_num, int dest_frame_num );
	void			move_shadow( int source_frame_num, int dest_frame_num );
	void			set_frame_base(t_int16 frame_num);	
	void			set_offset( t_screen_point const & new_offset );
	void			set_shadow_base(t_int16 frame_num);
	void			set_sequence_type(t_uint16 frame_num);
	void			toggle_base_frame_draw(bool val);

protected:
	// Types
	typedef t_bitmap_layer							t_layer_base;
	typedef t_image_sequence_frame_info				t_frame_info;
	typedef std::vector< t_frame_info >				t_frame_info_vector;
	typedef std::list<t_screen_rect>				t_rect_list;
	typedef std::vector<t_rect_list>				t_frame_rect_list_array;

	// Data members
	t_frame_info_vector		m_frame_info_vector;
	t_frame_rect_list_array	m_frame_rect_list_array;
	t_screen_point			m_offset;
	t_screen_rect			m_rect;
	t_screen_rect			m_shadow_rect;
	t_uint16				m_image_sequence_type;
	t_int16					m_base_frame;
	t_int16					m_base_shadow;
	bool					m_show_base_frames;

	// Member functions
	void							caluate_frame_rect_lists();
	virtual void					delete_layer( t_uint32 layer_index ) = 0;
	virtual t_layer_base &			get_layer_base( t_uint32 layer_index ) = 0;
	virtual t_layer_base const &	get_layer_base( t_uint32 layer_index ) const = 0;
	virtual t_uint32				get_layer_count() const = 0;
	t_frame_info_vector const &		get_frame_info_vector() const;
	void							resync_frames();

};

inline t_int16	t_image_sequence_base_base::get_base_frame() const
{
	return m_base_frame;
}

inline t_int16	t_image_sequence_base_base::get_base_shadow() const
{
	return m_base_shadow;
}

inline int t_image_sequence_base_base::get_frame_count() const
{
	return m_frame_info_vector.size();
}

inline t_screen_point t_image_sequence_base_base::get_offset() const
{
	return m_offset;
}

inline t_screen_rect t_image_sequence_base_base::get_rect() const
{
	return m_rect;
}

inline t_screen_rect t_image_sequence_base_base::get_rect( int frame_num ) const
{
	int frame_index = get_frame_index( frame_num );
	if ( frame_index == empty_frame_index() )
		return t_screen_rect( 0, 0, 0, 0 );
	
	t_screen_rect rect_composite = get_layer_base( frame_index ).get_rect();
	
	if (m_image_sequence_type == k_image_sequence_standard ||
		m_base_frame == k_image_no_base_frame)
		return rect_composite + get_offset();
	
	rect_composite = get_extent(rect_composite, get_layer_base( m_base_frame ).get_rect());
	
	return rect_composite + get_offset();
}

inline t_screen_rect t_image_sequence_base_base::get_shadow_rect() const
{
	return m_shadow_rect;
}

inline t_screen_rect t_image_sequence_base_base::get_shadow_rect( int frame_num ) const
{
	int frame_index = get_shadow_index( frame_num );
	if ( frame_index == empty_frame_index() )
		return t_screen_rect( 0, 0, 0, 0 );
	
	t_screen_rect rect_composite = get_layer_base( frame_index ).get_rect();
	
	if (m_image_sequence_type == k_image_sequence_standard ||
		m_base_shadow == k_image_no_base_frame)
		return rect_composite + get_offset();
	
	rect_composite = get_extent(rect_composite, get_layer_base( m_base_shadow ).get_rect());
	
	return rect_composite + get_offset();
}


inline bool t_image_sequence_base_base::hit_test(
												 int						frame_num,
												 t_screen_point const &	point ) const
{
	int frame_index = get_frame_index( frame_num );
	
	if (m_image_sequence_type == k_image_sequence_standard || m_base_frame == k_image_no_base_frame)
	{
		return	frame_index != empty_frame_index()
			&&	get_layer_base( frame_index ).contains( point - get_offset() );
	}
	else if ( m_image_sequence_type == k_image_sequence_delta_compressed)
	{
		return frame_index != empty_frame_index() &&
			(
				get_layer_base( frame_index ).contains( point - get_offset() )  ||
				get_layer_base( m_base_frame ).contains( point - get_offset() ) 
			);
	}
	else
		return false;
}

inline void t_image_sequence_base_base::set_offset(
	t_screen_point const & new_offset )
{
	t_screen_point old_offset = get_offset();
	
	m_offset = new_offset;
	m_rect += new_offset - old_offset;
	m_shadow_rect += new_offset - old_offset;
}

inline t_uint32 t_image_sequence_base_base::get_frame_index( int frame_num ) const
{
	assert( frame_num >= 0 && frame_num < (int)m_frame_info_vector.size() );
	return m_frame_info_vector[ frame_num ].index;
}

inline t_image_sequence_base_base::t_frame_info_vector const &
t_image_sequence_base_base::get_frame_info_vector() const
{
	return m_frame_info_vector;
}

inline t_uint32 t_image_sequence_base_base::get_shadow_index( int frame_num ) const
{
	assert( frame_num >= 0 && frame_num < (int)m_frame_info_vector.size() );
	return m_frame_info_vector[ frame_num ].shadow_index;
}

inline t_uint16 t_image_sequence_base_base::get_sequence_type() const
{
	return m_image_sequence_type;
}

inline void t_image_sequence_base_base::set_frame_base(t_int16 frame_num)
{
	m_base_frame = frame_num;
}

inline void t_image_sequence_base_base::set_shadow_base(t_int16 frame_num)
{
	m_base_shadow = frame_num;
}

inline void t_image_sequence_base_base::set_sequence_type(t_uint16 seq_type)
{
	m_image_sequence_type = seq_type;
}

inline void t_image_sequence_base_base::toggle_base_frame_draw(bool val)
{
	m_show_base_frames = val;
}

// ------------------------------------------------------------------------------
// t_image_sequence_base class template
// ------------------------------------------------------------------------------

template < typename t_bitmap_group_arg >
class t_image_sequence_base : public t_image_sequence_base_base
{
public:
	// Types
	typedef t_bitmap_group_arg							t_bitmap_group;
	typedef typename t_bitmap_group_arg::t_layer_type	t_frame;

	t_image_sequence_base();
	t_image_sequence_base( t_image_sequence_base_base const& base, 
		                   t_bitmap_group_arg         const& bitmaps );

	t_frame const *					get_frame_ptr( int frame_num ) const;

protected:
	// Types

	// Data members
	t_bitmap_group_arg	m_frames;

	// Member functions
	virtual void					delete_layer( t_uint32 layer_index );
	virtual t_layer_base &			get_layer_base( t_uint32 layer_index );
	virtual t_layer_base const &	get_layer_base( t_uint32 layer_index ) const;
	virtual t_uint32				get_layer_count() const;
	t_frame const *					get_frame_shadow_ptr( int frame_num ) const;
};

template < typename t_bitmap_group_arg >
inline t_image_sequence_base< t_bitmap_group_arg >::t_image_sequence_base()	     
{
	m_image_sequence_type	= k_image_sequence_standard;
	m_base_frame			= k_image_no_base_frame;
	m_base_shadow			= k_image_no_base_frame;
	m_show_base_frames		= true;
}

template < typename t_bitmap_group_arg > inline 
t_image_sequence_base< t_bitmap_group_arg >::t_image_sequence_base( 
											 t_image_sequence_base_base const& base,
											 t_bitmap_group_arg         const& bitmaps )
										   : t_image_sequence_base_base( base ),
										     m_frames( bitmaps )
{
	m_image_sequence_type	= base.get_sequence_type();
	m_base_frame			= base.get_base_frame();
	m_base_shadow			= base.get_base_shadow();
	m_show_base_frames		= true;
}

template < typename t_bitmap_group_arg >
void t_image_sequence_base< t_bitmap_group_arg >::delete_layer( t_uint32 layer_index )
{
	assert( layer_index >= 0 && layer_index < m_frames.size() );
	m_frames.erase( m_frames.begin() + layer_index );
}

template < typename t_bitmap_group_arg >
t_bitmap_layer &
t_image_sequence_base< t_bitmap_group_arg >::get_layer_base( t_uint32 layer_index )
{
	assert( layer_index >= 0 && layer_index < m_frames.size() );
	return m_frames[ layer_index ];
}

template < typename t_bitmap_group_arg >
t_bitmap_layer const &
t_image_sequence_base< t_bitmap_group_arg >::get_layer_base( t_uint32 layer_index ) const
{
	assert( layer_index >= 0 && layer_index < m_frames.size() );
	return m_frames[ layer_index ];
}

template < typename t_bitmap_group_arg >
t_uint32 t_image_sequence_base< t_bitmap_group_arg >::get_layer_count() const
{
	return m_frames.size();
}

template < typename t_bitmap_group_arg >
inline typename t_image_sequence_base< t_bitmap_group_arg >::t_frame const *
t_image_sequence_base< t_bitmap_group_arg >::get_frame_ptr( int	frame_num ) const
{
	t_uint32 frame_index = get_frame_index( frame_num );

	if ( frame_index == empty_frame_index() )
		return 0;
	return &m_frames[ frame_index ];
}

template < typename t_bitmap_group_arg >
inline typename t_image_sequence_base< t_bitmap_group_arg >::t_frame const *
t_image_sequence_base< t_bitmap_group_arg >::get_frame_shadow_ptr(
	int frame_num ) const
{
	t_uint32 frame_index = get_shadow_index( frame_num );

	if ( frame_index == empty_frame_index() )
		return 0;
	return &m_frames[ frame_index ];
}

// ------------------------------------------------------------------------------
// t_image_sequence_24 class
// ------------------------------------------------------------------------------

class t_image_sequence_24 : public t_image_sequence_base< t_bitmap_group_24 >
{
public:
	// Member functions
	bool						set_frames( t_bitmap_group_24 const & bitmap_group );
	bool						read( std::streambuf & stream );
	bool						read_version(
									std::streambuf &	stream,
									int					version,
									std::string *		owner_name_ptr = 0 );
	bool						write( std::streambuf & stream ) const;
	t_bitmap_group_24 &			get_frames();
	t_bitmap_group_24 const &	get_frames() const;

private:
	friend class t_image_sequence;

//	using t_image_sequence_base< t_image_sequence_24, t_bitmap_group_24 >::get_frame_info_vector;
};

inline t_bitmap_group_24 & t_image_sequence_24::get_frames()
{
	return m_frames;
}

inline t_bitmap_group_24 const & t_image_sequence_24::get_frames() const
{
	return m_frames;
}

inline bool read( std::streambuf & stream, t_image_sequence_24 & sequence )
{
	return sequence.read( stream );
}

// ------------------------------------------------------------------------------
// t_image_sequence class
// ------------------------------------------------------------------------------

class t_image_sequence : public t_image_sequence_base< t_bitmap_group >
{
public:
	// Constructors
	t_image_sequence();
	t_image_sequence( t_image_sequence_base_base const& base,
		              t_bitmap_group             const& bitmaps );
	explicit t_image_sequence(
		t_image_sequence_24 const &	other,
		t_screen_point const &		offset = t_screen_point( 0, 0 ) );
	t_image_sequence(
		t_image_sequence_24 const &	other,
		double						ratio,
		t_screen_point const &		offset = t_screen_point( 0, 0 ) );

	// Member functions
	void	draw_to(
				int						frame_num,
				t_screen_rect const &	source_rect,
				t_abstract_bitmap16 &	dest,
				t_screen_point const &	dest_point,
				int                     alpha = 15 ) const;
	void	draw_to(
				int						frame_num,
				t_abstract_bitmap16 &	dest,
				t_screen_point const &	dest_point,
				int                     alpha = 15) const;
	void	draw_shadow_to(
				int						frame_num,
				t_screen_rect const &	source_rect,
				t_abstract_bitmap16 &	dest,
				t_screen_point const &	dest_point,
				int						alpha = 15 ) const;
	void	draw_shadow_to(
				int						frame_num,
				t_abstract_bitmap16 &	dest,
				t_screen_point const &	dest_point,
				int						alpha = 15 ) const;
	
	
	void	draw_base_frame_to(
		t_screen_rect const &	source_rect,
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	dest_point,
		int                     alpha = 15 ) const;
	
	void draw_base_frame_to(
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	dest_point,
		int                     alpha = 15) const;
	
	
	void	draw_base_shadow_to(
		t_screen_rect const &	source_rect,
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	dest_point,
		int						alpha = 15 ) const;

	void	draw_base_shadow_to(
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	dest_point,
		int						alpha = 15 ) const;
};

inline t_image_sequence::t_image_sequence()
{
}

inline t_image_sequence::t_image_sequence( t_image_sequence_base_base const& base,
		                                   t_bitmap_group             const& bitmaps )
					   : t_image_sequence_base< t_bitmap_group >( base, bitmaps )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline void t_image_sequence::draw_base_frame_to(
						   t_screen_rect const &	source_rect,
						   t_abstract_bitmap16 &	dest,
						   t_screen_point const &	dest_point,
						   int						alpha ) const
{
	if (m_image_sequence_type == k_image_sequence_standard) 
		return;

	if (g_disable_base_frames_drawing == true) 
		return;
	
	if (m_base_frame == k_image_no_base_frame) 
		return;
	
	assert(m_base_frame < (int)m_frames.size());
	
	t_frame const * frame_ptr = &m_frames[ m_base_frame ];
	
	if ( frame_ptr != 0 )
		frame_ptr->draw_to( source_rect, dest, dest_point , alpha );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline void t_image_sequence::draw_base_frame_to(
										  t_abstract_bitmap16 &		dest,
										  t_screen_point const &	dest_point,
										  int						alpha ) const
{
	if (m_image_sequence_type == k_image_sequence_standard) 
		return;

	if (g_disable_base_frames_drawing == true) 
		return;
	
	if (m_base_frame == k_image_no_base_frame) 
		return;
	
	assert(m_base_frame < (int)m_frames.size());
	
	t_frame const * frame_ptr = &m_frames[ m_base_frame ];
	
	if ( frame_ptr != 0 )
		frame_ptr->draw_to( dest, dest_point, alpha );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline void t_image_sequence::draw_base_shadow_to(
							t_screen_rect const &	source_rect,
							t_abstract_bitmap16 &	dest,
							t_screen_point const &	dest_point,
							int						alpha ) const
{
	if (m_image_sequence_type == k_image_sequence_standard) 
		return;

	if (g_disable_base_shadow_drawing == true) 
		return;
	
	if (m_base_shadow == k_image_no_base_frame) 
		return;
	
	assert(m_base_shadow < (int)m_frames.size());
	
	t_frame const * frame_ptr = &m_frames[ m_base_shadow ];
	
	if ( frame_ptr != 0 )
		frame_ptr->draw_to( source_rect, dest, dest_point , alpha );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline void t_image_sequence::draw_base_shadow_to(
							t_abstract_bitmap16 &	dest,
							t_screen_point const &	dest_point,
							int						alpha ) const
{
	if (m_image_sequence_type == k_image_sequence_standard) 
		return;

	if (g_disable_base_shadow_drawing == true) 
		return;
	
	if (m_base_shadow == k_image_no_base_frame) 
		return;
	
	assert(m_base_shadow < (int)m_frames.size());
	
	t_frame const * frame_ptr = &m_frames[ m_base_shadow ];
	
	if ( frame_ptr != 0 )
		frame_ptr->draw_to( dest, dest_point, alpha );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline void t_image_sequence::draw_to(
	int						frame_num,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{

	draw_base_frame_to(source_rect - get_offset(), dest, 
			                dest_point + source_rect.top_left(), alpha);

	if (g_disable_frames_drwaing == true) 
		return;

	t_frame const * frame_ptr = get_frame_ptr( frame_num );
	if ( frame_ptr != 0 )
	{
		frame_ptr->draw_to( source_rect - get_offset(), dest, 
			                dest_point + source_rect.top_left(), alpha );
	}
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline void t_image_sequence::draw_to(
	int						frame_num,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	draw_base_frame_to(dest, dest_point + get_offset(), alpha );

	if (g_disable_frames_drwaing == true) 
		return;

	t_frame const * frame_ptr = get_frame_ptr( frame_num );
	if ( frame_ptr != 0 )
		frame_ptr->draw_to( dest, dest_point + get_offset(), alpha );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline void t_image_sequence::draw_shadow_to(
	int						frame_num,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	draw_base_shadow_to(source_rect - get_offset(), dest, 
			                dest_point + source_rect.top_left(), alpha);

	if (g_disable_shadow_drawing == true) 
		return;

	t_frame const * frame_ptr = get_frame_shadow_ptr( frame_num );
	if ( frame_ptr != 0 )
	{
		frame_ptr->draw_to( source_rect - get_offset(), dest, 
			                dest_point + source_rect.top_left(), alpha );
	}
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline void t_image_sequence::draw_shadow_to(
	int						frame_num,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	draw_base_shadow_to(dest,dest_point  + get_offset(),alpha);

	if (g_disable_shadow_drawing == true) 
		return;

	t_frame const * frame_ptr = get_frame_shadow_ptr( frame_num );
	if ( frame_ptr != 0 )
		frame_ptr->draw_to( dest, dest_point + get_offset(), alpha );
}

#endif // !defined( IMAGE_SEQUENCE_H_INCLUDED )
